/* SPDX-License-Identifier: GPL-2.0-or-later */
#ifndef DLMCOMMON_H
#define DLMCOMMON_H

#include "cluster/heartbeat.h"
#include "adl/adlapi.h"

/* Valid lock modes. */
#define LKM_NLMODE      0               /* null lock */
#define LKM_PRMODE      3               /* protected read */
#define LKM_EXMODE      5               /* exclusive */

#define ADL_HB_NODE_DOWN_PRI     (0xf000000)
#define ADL_HB_NODE_UP_PRI       (0x8000000)
#define ADL_TIMEOUT_PRI          (0x1000000)  /* OCFS2_TIMEOUT_PRI - 1 */

#define LOG_RATELIMIT_INTERVAL_MSES (10 * 1000)

enum caw_mode {
	MODE_LOCK = 0,
	MODE_UNLOCK,
	MODE_PENDING,
	NUM_MODE
};

struct adl_debug_ctxt {
	struct dentry *debug_state_dentry;
};

#define ADL_MAX_NODES		64
#define LOCK_MAX_HOLDERS	ADL_MAX_NODES

/* ocfs2_adl_lock.lk_flags */
#define ADL_LOCK_INVALID		0x00
#define ADL_LOCK_LOCKED			0x01
#define ADL_LOCK_USER_MODE		0x02
#define ADL_LOCK_KERNEL_MODE	0x04
#define ADL_LOCK_MODE_MASK		(ADL_LOCK_USER_MODE | ADL_LOCK_KERNEL_MODE)
#define ADL_LOCK_FLAGS_MASK		\
	(ADL_LOCK_INVALID | ADL_LOCK_LOCKED | ADL_LOCK_USER_MODE | \
	ADL_LOCK_KERNEL_MODE)

/* ocfs2_adl_lock.lk_level */
#define DISK_LOCK_LEVEL_NL		0x00
#define DISK_LOCK_LEVEL_PR		0x01
#define DISK_LOCK_LEVEL_EX		0x02
#define DISK_LOCK_LEVEL_MASK	\
	(DISK_LOCK_LEVEL_NL | DISK_LOCK_LEVEL_PR | DISK_LOCK_LEVEL_EX)

#define ADL_INVALID_INDEX		LOCK_MAX_HOLDERS

#define LOCK_BLOCK_SIGNATURE		"LOCK01"

enum {
	ADL_QUERY_LOCK_MSG    = 600,
	ADL_UNLOCK_LOCK_MSG   = 601,
	ADL_VALET_LOCK_MSG    = 602, /* reserved */
	ADL_RELEASE_TRUNCATE_LOG_MSG  = 603,
};

struct adl_lock_packet {
	__be16 sender_idx;
	u8 sector_offset;
	u8 pad1[5];
	__be64 blkno;
};

typedef void (adl_async_msg_cb)(void *, int);

struct adl_async_msg_priv {
	u32                     am_type;
	u32                     am_len;
	u16                     am_target_node;
	adl_async_msg_cb *am_callback;
};

struct adl_work_item;
typedef void (adl_workfunc_t)(struct adl_work_item *, void *);
struct adl_work_item {
	struct list_head list;
	adl_workfunc_t *func;
	struct adl_ctxt *adl;
	void *msg;  /* caller should free item->msg in xxxx_worker */
	union {
		struct adl_async_msg_priv am;
	} u;
};

struct ocfs2_adl_lock {
/*00*/	__u8 lk_signature[7]; /* LOCK01 */
	__u8 lk_level;		/* 0x01 - pr; 0x02 - ex */
	__le64 lk_generation;			/* monotone increasing */

/*10*/	__le16 lk_host_id[LOCK_MAX_HOLDERS];	/* 128 bytes, 2 byte per lock */
/*90*/	__le32 lk_hb_gen[LOCK_MAX_HOLDERS];	/* 256 bytes, 4 byte per lock */
/*190*/	__u8 lk_flags[LOCK_MAX_HOLDERS];	/* use 3 bit per lock. 01 - locked;
							* 02 - user_mode; 04 - kernel_mode */
		__le16	lk_pending_host;
		__le16	pad1;
		__le32	lk_pending_hb_gen;
		__le32 seqno;
		__le64 pad2[4];
		__le32 pad3;
};

static inline u16 hostid_to_key(u16 id)
{
	return id == 0 ? O2NM_MAX_NODES : id;
}

static inline u16 host_id(struct ocfs2_adl_lock *lock, int i)
{
	return le16_to_cpu(lock->lk_host_id[i]);
}

static inline u32 host_hb_gen(struct ocfs2_adl_lock *lock, int i)
{
	return le32_to_cpu(lock->lk_hb_gen[i]);
}

static inline u8 host_flags(struct ocfs2_adl_lock *lock, int i)
{
	return lock->lk_flags[i];
}

static inline u16 pending_host_id(struct ocfs2_adl_lock *lock)
{
	return le16_to_cpu(lock->lk_pending_host);
}

static inline u32 pending_host_hb_gen(struct ocfs2_adl_lock *lock)
{
	return le32_to_cpu(lock->lk_pending_hb_gen);
}

static inline int lock_free(struct ocfs2_adl_lock *lock, int i)
{
	return (lock->lk_flags[i] == ADL_LOCK_INVALID);
}

static inline int lock_locked(struct ocfs2_adl_lock *lock, int i)
{
	return (lock->lk_flags[i] & ADL_LOCK_LOCKED);
}

static inline int lock_mode(struct ocfs2_adl_lock *lock, int i)
{
	return (lock->lk_flags[i] & ADL_LOCK_MODE_MASK);
}

static inline int test_lock_mode(struct ocfs2_adl_lock *lock, int i,
		int mode)
{
	return ((lock->lk_flags[i] & mode) == mode);
}

static inline int same_lock_mode(struct ocfs2_adl_lock *lock, int i,
		int mode)
{
	return (lock_mode(lock, i) == mode);
}

static inline void clear_lock_mode(struct ocfs2_adl_lock *lock, int i,
		int mode)
{
	lock->lk_flags[i] &= ~mode;
}

typedef int (*lock_valid_func)(struct adl_ctxt *adl,
		struct ocfs2_adl_lock *lock, int i);

static inline int lock_valid(struct adl_ctxt *adl,
		struct ocfs2_adl_lock *lock, int i)
{
	int valid;
	u16 hostid = host_id(lock, i);

	spin_lock(&adl->spinlock);
	valid = test_bit(hostid, adl->live_nodes_map) &&
			(host_hb_gen(lock, i) == *adl->HBGen[hostid]);
	spin_unlock(&adl->spinlock);

	return valid;
}

static inline int lock_valid_wait_for_recovery(struct adl_ctxt *adl,
		struct ocfs2_adl_lock *lock, int i)
{
	int valid;
	u16 hostid = host_id(lock, i);

	spin_lock(&adl->spinlock);
	valid = test_bit(hostid, adl->recovery_map) ||
			(test_bit(hostid, adl->live_nodes_map) &&
			(host_hb_gen(lock, i) == *adl->HBGen[hostid]));
	spin_unlock(&adl->spinlock);

	return valid;
}

static inline int lock_pending_valid(struct adl_ctxt *adl,
		struct ocfs2_adl_lock *lock)
{
	int valid;
	u16 hostid = pending_host_id(lock);

	spin_lock(&adl->spinlock);
	valid = test_bit(hostid, adl->live_nodes_map) &&
			(pending_host_hb_gen(lock) == *adl->HBGen[hostid]);
	spin_unlock(&adl->spinlock);

	return valid;
}

static inline int lock_level(struct ocfs2_adl_lock *lock)
{
	return (lock->lk_level & DISK_LOCK_LEVEL_MASK);
}

static inline int pr_locked(struct ocfs2_adl_lock *lock)
{
	return (lock_level(lock) == DISK_LOCK_LEVEL_PR);
}

static inline int ex_locked(struct ocfs2_adl_lock *lock)
{
	return (lock_level(lock) == DISK_LOCK_LEVEL_EX);
}

static inline int to_disk_level(int level)
{
	if (level == LKM_PRMODE)
		return DISK_LOCK_LEVEL_PR;
	else if (level == LKM_EXMODE)
		return DISK_LOCK_LEVEL_EX;
	else
		return DISK_LOCK_LEVEL_NL;
}

static inline void set_lock_level(struct ocfs2_adl_lock *lock, int level)
{
	lock->lk_level = to_disk_level(level);
}

static inline void add_locked_lock(struct adl_ctxt *adl,
		struct ocfs2_adl_lock *lock, int i, int lock_mode)
{
	lock->lk_flags[i] |= (ADL_LOCK_LOCKED | lock_mode);
	lock->lk_host_id[i] = cpu_to_le16(adl->node_num);

	spin_lock(&adl->spinlock);
	lock->lk_hb_gen[i] = cpu_to_le32(*adl->HBGen[adl->node_num]);
	spin_unlock(&adl->spinlock);
}

static inline void add_pending_lock(struct adl_ctxt *adl,
		struct ocfs2_adl_lock *lock)
{
	lock->lk_pending_host = cpu_to_le16(adl->node_num);
	spin_lock(&adl->spinlock);
	lock->lk_pending_hb_gen = cpu_to_le32(*adl->HBGen[adl->node_num]);
	spin_unlock(&adl->spinlock);
}

static inline void clear_one_lock(struct adl_ctxt *adl,
		struct ocfs2_adl_lock *lock, int i)
{
	lock->lk_flags[i] = ADL_LOCK_INVALID;
	lock->lk_host_id[i] = cpu_to_le16(O2NM_INVALID_NODE_NUM);
	lock->lk_hb_gen[i] = 0;
}

static inline void clear_pending_lock(struct ocfs2_adl_lock *lock)
{
	lock->lk_pending_host = cpu_to_le16(O2NM_INVALID_NODE_NUM);
	lock->lk_pending_hb_gen = 0;
}

static inline int adl_is_invalid(struct adl_ctxt *adl)
{
	int invalid;

	spin_lock(&adl->spinlock);
	invalid = adl->orphan;
	spin_unlock(&adl->spinlock);
	return invalid;
}

static inline int adl_lock_pending_exist(struct adl_ctxt *adl,
		struct ocfs2_adl_lock *lock)
{
	if (lock_pending_valid(adl, lock))
		return 1;
	else
		return 0;
}

static inline int adl_lock_other_pending_exist(struct adl_ctxt *adl,
		struct ocfs2_adl_lock *lock)
{
	if (lock_pending_valid(adl, lock) &&
			adl->node_num != pending_host_id(lock))
		return 1;
	else
		return 0;
}

static inline void adl_lock_initialize(struct ocfs2_adl_lock *lock)
{
	int i;

	strcpy(lock->lk_signature, LOCK_BLOCK_SIGNATURE);

	for (i = 0; i < LOCK_MAX_HOLDERS; i++)
		lock->lk_host_id[i] = cpu_to_le16(O2NM_INVALID_NODE_NUM);
	lock->lk_pending_host = cpu_to_le16(O2NM_INVALID_NODE_NUM);
}

int adl_release_truncate_log_request_handler(struct o2net_msg *msg, u32 len, void *data,
		void **ret_data);
int adl_query_lock_request_handler(struct o2net_msg *msg, u32 len, void *data,
		void **ret_data);
int adl_unlock_lock_handler(struct o2net_msg *msg, u32 len, void *data,
		void **ret_data);
void adl_fire_callbacks(struct adl_ctxt *adl, enum adl_cb_type type,
		u64 blkno, int sector_offset);
int adl_send_async_msg(struct adl_ctxt *adl, u32 msg_type,
		void *msg, u32 len,
		u16 target_node, adl_async_msg_cb *callback);
struct adl_ctxt *adl_grab(struct adl_ctxt *adl);
void adl_put(struct adl_ctxt *adl);
int adl_is_node_authorized(struct adl_ctxt *adl, u16 node);

#endif
